/*
 * Copyright 2012 Finbudget.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package finbudgettable.client.ui.grid.dnd;

import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.DOM;
import finbudget.client.ui.table.Record;
import finbudgettable.client.ui.grid.CellGrid;
import finbudgettable.client.ui.grid.column.GridHeaderCell;

public class DropHelper implements DropTarget<Column<Record, ?>> {

    private final CellGrid grid;
    private final Column<Record, ?> targetColumn;
    private final GridHeaderCell cell;

    private boolean overRightElement;
    private DivElement rightDiv;

    private boolean overLeftElement;
    private DivElement leftDiv;

    public DropHelper(CellGrid grid, Column<Record, ?> targetColumn, GridHeaderCell cell) {
        this.grid = grid;
        this.targetColumn = targetColumn;
        this.cell = cell;
    }

    private Element getTdElement() {
        Element el = cell.getCaptionElement();
        while (el.hasParentElement()) {
            el = el.getParentElement();
            if (el == null) {
                return null;
            }
            if ("th".equalsIgnoreCase(el.getTagName())) {
                return el;
            }
        }
        return null;
    }

    private boolean isColumnVisible() {
        return grid.isColumnVisible(targetColumn);
    }

    @Override
    public void onDragStart(Draggable<Column<Record, ?>> draggable) {
        if (!isColumnVisible()) {
            return;
        }
        Element td = getTdElement();
        if (td == null) {
            return;
        }
        String dropZoneClassName = DragHelper.RESOURCES.style().bDropzone();

        int height = td.getAbsoluteBottom() - td.getAbsoluteTop() - 1;

        leftDiv = DOM.createDiv().cast();
        leftDiv.getStyle().setPosition(Style.Position.ABSOLUTE);
        leftDiv.setClassName(dropZoneClassName);
        leftDiv.getStyle().setTop(0, Style.Unit.PX);
        if (isFirstColumn()) {
            leftDiv.getStyle().setLeft(0, Style.Unit.PX);
        } else {
            leftDiv.getStyle().setLeft(-10 / 2, Style.Unit.PX);
        }
        leftDiv.getStyle().setHeight(height, Style.Unit.PX);
        td.getFirstChildElement().appendChild(leftDiv);

        if (isLastColumn()) {
            int right = td.getAbsoluteRight() - td.getAbsoluteLeft();
            rightDiv = DOM.createDiv().cast();
            rightDiv.setClassName(dropZoneClassName);
            rightDiv.getStyle().setHeight(height, Style.Unit.PX);
            rightDiv.getStyle().setPosition(Style.Position.ABSOLUTE);
            rightDiv.getStyle().setTop(0, Style.Unit.PX);
            rightDiv.getStyle().setLeft(right - 10, Style.Unit.PX);
            td.getFirstChildElement().appendChild(rightDiv);
        }
    }

    private boolean isLastColumn() {
        return grid.getColumn(grid.getColumnCount() - 1) == targetColumn;
    }

    private boolean isFirstColumn() {
        return grid.getColumnIndex(targetColumn) == 0;
    }

    @Override
    public void onDrag(Draggable<Column<Record, ?>> draggable, int x, int y) {
        if (isColumnVisible()) {
            if (leftDiv != null) {
                overLeftElement = processDragOver(leftDiv, x, y);
            }
            if (rightDiv != null) {
                overRightElement = processDragOver(rightDiv, x, y);
            }
        }
    }

    /**
     * if cursor is over element returns true
     */
    private boolean processDragOver(DivElement element, int x, int y) {
        String className = DragHelper.RESOURCES.style().bDropzoneActive();
        boolean overElement = isOverElement(element, x, y);
        if (overElement) {
            element.addClassName(className);
        } else {
            element.removeClassName(className);
        }
        return overElement;
    }

    private boolean isOverElement(Element element, int x, int y) {
        return element.getAbsoluteTop() <= y && y < element.getAbsoluteBottom()
                && element.getAbsoluteLeft() <= x && x < element.getAbsoluteRight();
    }

    @Override
    public void onDragEnd(Draggable<Column<Record, ?>> draggable) {
        if (!isColumnVisible()){
            return;
        }
        if (leftDiv != null) {
            leftDiv.removeFromParent();
            leftDiv = null;
            if (overLeftElement) {
                Column<Record, ?> draggingColumn = draggable.getValue();
                if (!draggingColumn.equals(targetColumn)) {
                    boolean dropToNextColumn = grid.getColumnIndex(draggingColumn) + 1 == grid.getColumnIndex(targetColumn);
                    if (!dropToNextColumn) {
                        grid.moveColumn(draggingColumn, grid.getColumnIndex(targetColumn));
                    }
                }
            }
        }
        if (rightDiv != null) {
            rightDiv.removeFromParent();
            rightDiv = null;
            if (overRightElement) {
                Column<Record, ?> draggingColumn = draggable.getValue();
                grid.moveColumn(draggingColumn, grid.getColumnCount());
            }
        }
    }

}